package net

import (
	"gitee.com/CloudGuan/rpc-go-backend/idlrpc/common"
	"net"
	"sync"
	"sync/atomic"
	"time"
)

type IdlConn interface {
	GetConnID() uint64
	GetRemoteAddr() string
	SetHandle(ch ConnHandle)
	Send(pkg []byte) //发送协议
	Close()
}

type connInfo struct {
	connId    uint64      //连接id
	status    uint32      //是否关闭标志位
	sendQueue chan []byte //网络
	ch        ConnHandle  //保证只有主协程修改
	wg        sync.WaitGroup
	conn      *net.TCPConn //网络net包连接
}

func (c *connInfo) GetConnID() uint64 {
	return c.connId
}

func (c *connInfo) GetRemoteAddr() string {
	return c.conn.RemoteAddr().String()
}

//@title 外部调用发送关闭
func (c *connInfo) Close() {
	atomic.StoreUint32(&c.status, CLOSED)
	c.wg.Wait()
	c.doClose()
}

func (c *connInfo) SetHandle(ch ConnHandle) {
	c.ch = ch
}

func (c *connInfo) Send(pkg []byte) {
	c.sendQueue <- pkg
}

//@title 关闭连接函数 框架调用
func (c *connInfo) doClose() {
	//原子操作写入
	atomic.StoreUint32(&c.status, CLOSED)
	//TODO 等待发送 接收协程关闭
	//告知上层调用完毕
	c.ch.OnClose()
	//一切结束后回收资源
	c.conn.Close()
}

func (c *connInfo) startLoop() {
	atomic.StoreUint32(&c.status, CONNECTED)
	go c.recvmsg()
	go c.sendmsg()
}

func (c *connInfo) recvmsg() {
	defer func() {
		c.conn.Close()
		c.wg.Done()
	}()

	var bufflen int
	var err error
	buffer := make([]byte, 1024)
	c.wg.Add(1)
	for {
		if atomic.LoadUint32(&c.status) != CONNECTED {
			break
		}

		bufflen, err = c.conn.Read(buffer)
		if err != nil {
			if common.IsNoDataError(err) {
				// 没有收到数据
				//TODO 添加日志模块
				continue
			}
			if common.IsNetOpError(err) {
				c.conn.CloseRead()
				return
			}
			if common.IsNetEofError(err) {
				//TODO add log
				return
			}
			c.conn.CloseRead()
			return
		}

		pkg := make([]byte, bufflen)
		pkglen := copy(pkg, buffer[:bufflen])
		if pkglen != bufflen {
			//TODO add panic to copy error!!
			panic("copy recv buffer is error!!!!!")
			return
		}

		recvObj := func() {
			//调用告知handle 主线程写入 所以要拷贝一次
			c.ch.OnRecv(pkg)
		}
		jobQueue <- recvObj
	}
}

// 协程发送消息 关闭时候不管那么多直接 暴力关闭 没有优雅退出
func (c *connInfo) sendmsg() {
	defer func() {
		c.wg.Done()
	}()

	c.wg.Add(1)
	for {
		if atomic.LoadUint32(&c.status) != CONNECTED {
			break
		}
		buffer, more := <-c.sendQueue
		if more == false {
			//TODO add log for send channel, it has been closed
			return
		}

		if len(buffer) <= 0 {
			time.Sleep(10)
			continue
		}
		//这里可能读出来是空，说明缓冲区里面没有数据
		sendlen, err := c.conn.Write(buffer)
		if sendlen != len(buffer) {
			c.conn.CloseWrite()
			break
		}

		if err != nil {
			//TODO close network
			c.conn.CloseWrite()
			break
		}
	}
}
